﻿#define DKISDEBUGGING 

using System;
using System.Net;
using System.Net.Sockets;
using System.Threading;
using System.Threading.Tasks;
using System.Linq;
using System.Collections.Generic;
using System.Text;

namespace NMbsDuplex.TCPServerX.Deamon
{
    using DrDimsLib.CoreFoundation.Functionality;
    using System.IO;

    public sealed class Bifrost : TCPBase
    {
        /// <summary>
        /// Bifrost 北欧神话中的【彩虹桥】
        /// </summary>
        /// <param name="port"> 程序初始化绑定Tcp端口号 </param>
        public Bifrost(Int32 port) : base(port)
        {
            // 构造一个本地 公网+本地网卡 多重绑定
            Listener.Bind(new IPEndPoint(IPAddress.Loopback, port));
            Listener.Listen(ManagedQueueLength /* 默认的TCP排队等待长度 */);

            // 日志系统
            BootStrap4LogSys();

            // 消息队列推送管道 - 作为主进程的一个子线程充当控制台服务器的消息推送管道
            var bgThread = new Thread(() =>
            {
                MsgPushingEvent();
            });
            bgThread.Start();

            // Service服务系统 Ω双工Ω
            while (true)
            {
                // 添加用户到 | 数.据.链.表 |
                Socket forkChild = Listener.Accept();
                RegisterDict4Domain[workItemTags.NodeLargen](forkChild);

                Task.Run(() =>
                {
                    Push1st(forkChild); // Greeting
                }).ContinueWith(task =>
                {
                    MsgPullingEvent(forkChild);
                });
            }
        }

        /// <summary>
        /// 处理Server端的**消息推送管道** 宿主为异步后台线程
        /// </summary>
        private void MsgPushingEvent()
        {
            var buffer = new byte[BUFSIZ];
            string ServMsg = string.Empty;
            while ((ServMsg = Console.ReadLine()) != null)
            {
                // 链表长度检查 - 使用指针地址节点Node是没有一丁点性能损耗的  
                // 广东人喜欢用List<Node> 我觉得是非常愚蠢的
                if (LinkedInUsers.Count == 0)
                {
                    Console.WriteLine("链表节点为空 跳过无效推送...");
                    continue;
                }
                // 推送的V1版本为同步 数据结构为`双向链表` 访问方式为轮训
                LinkedInUsers.ToList().ForEach(Node =>
                {
                    // *** 消息队列推送管道 *** 
                    Socket sock = Node.Value.refSock;
                    sock.SendBytes("ServPush[消息推管道]~>: " + ServMsg,TXTCoding);
                });
                Thread.Sleep(PushInterval);
            }
        }
        /// <summary>
        /// Socket消息处理主函数 根据自然语言传输协议 进行逻辑分路
        /// </summary>
        /// <param name="SockRef"> SockRef文件描述符 </param>
        private void MsgPullingEvent(Socket SockRef)
        {
            // 2.Server端Pull异步接受线程 1:1
            int N = 0;
            while (true)
            {
                string receivingMsg = SockRef.RecvBytes(TXTCoding, ref N);
                if (N > 0)
                {
                    if (BLLGo(SockRef, receivingMsg) != true)
                    {
                        Console.WriteLine("Server端口正常断开");
                        return;
                    }
                }
                else
                {
                    /* 网卡缓冲区异常 - `漏水处理` */
                    RegisterDict4Domain[workItemTags.NodeSqueeze](SockRef);
                    Console.WriteLine("漏水处理");
                    break;
                }
            }
        }

        /// <summary>
        /// Socket传输解码层 - 重写BLL传输应用层的业务逻辑处理函数
        /// </summary>
        /// <param name="refSock"> 文件描述符 </param>
        /// <param name="receivingMsg"> 传输字节流 </param>
        /// <returns></returns>
        protected internal override bool BLLGo(Socket refSock, string receivingMsg)
        {
            bool isContinue = base.BLLGo(refSock, receivingMsg);
            // 这里 将会产生大量的业务逻辑代码 推荐使用 1.依赖注入 2.函数指针字典
            /* 停止用水 - 客户端主动关闭 */

            if (receivingMsg.StartsWith(Tokenizer.TPIN))
            {
                HandlePing(refSock, receivingMsg);
            }else if (receivingMsg.StartsWith(Tokenizer.TSyn))
            {

            }else if (receivingMsg.StartsWith(Tokenizer.TFin))
            {
                RegisterDict4Domain[workItemTags.NodeSqueeze](refSock);
                isContinue = false;
            }else if (receivingMsg.StartsWith(Tokenizer.TBiz))
            {

            }else if (receivingMsg.StartsWith(Tokenizer.TFile))
            {
                string rs = translateFile(refSock,receivingMsg);
                Console.WriteLine(rs);
            }else 
            {
                var pingInfo = "未经过协议解析 `空Ping`";
                HandlePing(refSock, pingInfo);
            }
            return isContinue;
        }

        /// 首次接入 Server推送一次欢迎信息      
        private void Push1st(Socket SockBlockPtr)
        {
            // Socket底层扩展基础函数调用 - 发送字节码
            var GreetingTCP = string.Format("欢迎来到我的`双工客户端`");
            GreetingTCP += GREETING + Environment.NewLine;
            SockBlockPtr.SendBytes(GreetingTCP, TXTCoding);
        }

        /// <summary>
        /// Socket空ping回显处理函数
        /// </summary>
        /// <param name="sockRef"> 文件描述符 </param>
        /// <param name="receivingMsg"> 传输消息流 </param>
        private void HandlePing(Socket sockRef, string receivingMsg)
        {
            var message = string.Format("[{0}]---> {1}.", sockRef.RemoteEndPoint, receivingMsg);
            Console.Beep();
            Console.WriteLine(receivingMsg);
            sockRef.SendBytes(message, TXTCoding);
        }

        /// <summary>
        /// 文件传输管道 步进式传输 1.发送文件清单 2.传输文件管道 3.发送传输完毕指令集
        /// </summary>
        /// <param name="sockRef"> 文件描述符 </param>
        /// <returns> 传输总字节数转换之后的自然语言描述 </returns>
        private string translateFile(Socket sockRef,string receivingMsg)
        {
            IEnumerable<StreamInfo> results = new IOUtilWorkFlow().ScanFolderV2(null, false);
            var tokens = receivingMsg.Split(Convert.ToChar(Tokenizer.TSeper));
            var steps = new string[] { "1.发送清单", "2.接收等待", "3.开启传输", "4.发送结束符" };
            Console.WriteLine(tokens.Length + 0);

            // 1.发送清单 2.接收等待 3.开启传输 4.发送结束符
            if (tokens.Length == 1)
            {
                var PreHeaderLists = new StringBuilder();
                PreHeaderLists.AppendLine(Tokenizer.DKLine);
                int index = 1;
                results.ToList().ForEach(fileInfo => {
                    var Line = string.Format("{0}.`{1}`",index++,fileInfo.StreamPath);
                    PreHeaderLists.AppendLine(Line);
                });
                PreHeaderLists.AppendLine(Tokenizer.DKLine);
                sockRef.SendBytes(PreHeaderLists.ToString(), TXTCoding);
                return steps[0];

            }else if(tokens.Length == 2)
            {
                string fileURL = Environment.GetFolderPath(Environment.SpecialFolder.Desktop);

                BFileTransMethod FTransMode;
                string voidInfo = string.Empty;
                if (!string.IsNullOrEmpty(tokens.ElementAt(1)))
                {
                    var SELIndex = Convert.ToInt32(tokens.ElementAt(1));
                    fileURL += Path.DirectorySeparatorChar;
                    fileURL += results.ElementAt(SELIndex-1).hook.Name;
                }
                else
                {
                    string s = string.Format("`{0}`协议错误 没有`{1}`分隔符",
                        Tokenizer.TFile, Tokenizer.TSeper);
                    sockRef.SendBytes(s, TXTCoding);
                }                
                // IO异常检测
                DirectoryInfo dir = new DirectoryInfo(fileURL);
                if (dir.Exists) return "不能传输Dir";
                FileInfo Finfo = new FileInfo(fileURL);
                if (!Finfo.Exists) return "文件不存在";

                long LenStream = Finfo.Length;
                if (LenStream < 1024 * 32)  /* [0~32kb] */
                {
                    FTransMode = BFileTransMethod.SendByRawSocket;
                    long s = LenStream / (long)Math.Pow(1024, 1);
                    voidInfo = string.Format("\t小文件--->[{0}]大小[{1:F}]Kb", fileURL, s);
                    sockRef.SendFile(fileURL);
                }
                else if (LenStream <= 1024 * 1024 * 32) // [32Kb~32Mb]
                {
                    FTransMode = BFileTransMethod.SendOnce;
                    long s = LenStream / (long)Math.Pow(1024, 2);
                    long code = sockRef._TransFile(fileURL,FTransMode);
                    voidInfo = string.Format("\t中文件--->[{0}]大小[{1:F}]Mb", fileURL, s);
                    voidInfo += Environment.NewLine + string.Format("传输字节码[{0}]", code);
                }
                else if (LenStream <= 1024 * 1024 * 1024 * 1) // [32Mb ~ 1Gb]
                {
                    FTransMode = BFileTransMethod.SendByTask;
                    long s = LenStream / (long)Math.Pow(1024, 2);
                    voidInfo = string.Format("\t大文件--->[{0}]大小[{1:F}]Gb", fileURL, s);
                    long code = sockRef._TransFile(fileURL, FTransMode);
                    voidInfo += Environment.NewLine + string.Format("传输字节码[{0}]", code);
                }
                else // 超大文件 [ >= 1.0Gb]
                {
                    FTransMode = BFileTransMethod.Big5;
                    voidInfo = " 超大文件 [ >= 1.0Gb]";
                    long code = sockRef._TransFile(fileURL, FTransMode);
                    voidInfo += Environment.NewLine + string.Format("传输字节码[{0}]", code);
                }
                return steps[1];
            }
            return steps.Last();
        }
    }
}
